package com.bajiao.app.filter;

import com.bajiao.app.shiro.vo.DefContants;
import com.bajiao.common.api.vo.RestResult;
import com.bajiao.common.constant.CommonConstant;
import com.bajiao.common.system.util.JwtUtil;
import com.bajiao.common.util.RedisUtil;
import com.bajiao.common.util.jwt.JWTHelper;
import com.bajiao.common.util.jwt.JwtInfo;
import com.bajiao.common.util.oConvertUtils;
import com.bajiao.entity.user.User;
import com.bajiao.service.user.UserService;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import org.yaml.snakeyaml.Yaml;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author
 * @create 2024-10-10
 * @desc   鉴权登录拦截器
 **/
@Slf4j
@WebFilter(filterName = "apiFilter",urlPatterns = {"/api/*","/pc/*"})
public class ApiFilter implements Filter {

	@Autowired
	private RedisTemplate redisTemplate;

	@Value(value = "${server.servlet.context-path}")
	private String projectName;

	@Value(value = "${jwt.pub-key.path}")
	private String pubKeyPath;

	@Autowired
	private RedisUtil redisUtil;

	private List<String> filterlist;

	@Autowired
	private UserService userService;

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{

		Integer type ;

		Map<String,String> params=new HashMap();

		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		HttpServletResponse httpServletResponse = (HttpServletResponse) response;
		httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
		httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
		httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
		// 跨域时会首先发送一个option请求，这里我们给option请求直接返回正常状态
		if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
			httpServletResponse.setStatus(HttpStatus.OK.value());
			return ;
		}
		HttpSession session = httpServletRequest.getSession();
		String currPath = httpServletRequest.getRequestURI();    //当前请求的URL
		currPath = currPath.replace(projectName,""); //去掉项目名，以api开头

//		log.info("请求接口》》》》》》{}，参数：{}",currPath,JSON.toJSONString(request.getParameterMap()));

//		//接口参数加密
//		HttpServletRequest servletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//		//获取前端请求签名
//		String requestSign = servletRequest.getHeader("sign");
//
//		if (StringUtils.isBlank(requestSign)) {
//			returnJson(response,3);
//			return;
//		}
//
//		//获取前端请求参数
//		Map<String, String[]> map = request.getParameterMap();
//
//		map.forEach((k,v)->{
//			String[] strArr = v;
//			if(strArr.length>0){
//				params.put(k,strArr[0]);
//			}
//		});
//
//		//获取加密后的签名
//		String encryptionSign = SignUtil.getSignInfo(params, "");
//
//		//比较是否相等
//		if (!StringUtils.equals(requestSign,encryptionSign)) {
//			returnJson(response,3);
//			return;
//		}

		if (filterlist.size() != 0) {
			for (String s : filterlist) {
				if (s.equals(currPath)) {
					chain.doFilter(httpServletRequest, httpServletResponse);
					return;
				}
				//匹配/**
				if (s.contains("/**")) {
					String path = s.replace("/**", "");
					if (currPath.contains(path)) {
						chain.doFilter(httpServletRequest, httpServletResponse);
						return;
					}
				}

			}
		}
		//假如token为空返回
		String token = httpServletRequest.getHeader(DefContants.X_ACCESS_TOKEN);
		if (StringUtils.isBlank(token)) {
			returnJson(response);
			return;
		}

		JwtInfo jwtInfo = null;
		try {
			jwtInfo = JWTHelper.getInfoFromToken(token, pubKeyPath);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//判断是后台用户访问还是APP用户访问
		if (jwtInfo != null) {
			//查询用户信息
			User userInfo = (User) redisUtil.get(CommonConstant.SIGN_USER_ID + jwtInfo.getUserId());
			if (userInfo == null) {
				returnJson(response);
				return;
			}
			Object currentTokenValue = redisUtil.get(CommonConstant.APP_USER_TOKEN + jwtInfo.getUserId() + ":" + jwtInfo.getClient());
			if(currentTokenValue == null){
				returnJson(response);
				return;
			}
			String currentToken = (String)currentTokenValue;
			if(!currentToken.equals(token)){
				returnJson(response);
				return;
			}

		} else {
			returnJson(response);
			return;
		}

		//如果token验证成功，将token对应的用户id存在request中，便于之后注入
		request.setAttribute(CommonConstant.SIGN_USER_ID, jwtInfo.getUserId());
		chain.doFilter(httpServletRequest, httpServletResponse);
		return;

	}


	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		//从配置文件读取添加不需要token的路径
		//		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		Yaml yaml = new Yaml();
		InputStream inputStream = ApiFilter.class.getClassLoader().getResourceAsStream("noneed-login.yml");
		//System.out.println(url.toString());

		HashMap hashMap = yaml.loadAs(inputStream, HashMap.class);
		try {
			filterlist = (List)hashMap.get("filterlist");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void destroy() {
	}

	private void returnJson(ServletResponse response) {
		String time = String.valueOf(System.currentTimeMillis());
		PrintWriter writer = null;

		RestResult responseBean = new RestResult();
		responseBean.setCode(2);
		responseBean.setData(null);
		responseBean.setMsg("token过期");
		responseBean.setTime(time);
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/json; charset=utf-8");
		try {
			JSONObject myJson = JSONObject.fromObject(responseBean);
			writer = response.getWriter();
			writer.print(myJson);

		} catch (Exception e) {
			//log.error("response error",e);
		} finally {
			if (writer != null)
				writer.close();
		}
	}

	/**
	 * JWTToken刷新生命周期 （解决用户一直在线操作，提供Token失效问题）
	 * 1、登录成功后将用户的JWT生成的Token作为k、v存储到cache缓存里面(这时候k、v值一样)
	 * 2、当该用户再次请求时，通过JWTFilter层层校验之后会进入到doGetAuthenticationInfo进行身份验证
	 * 3、当该用户这次请求JWTToken值还在生命周期内，则会通过重新PUT的方式k、v都为Token值，缓存中的token值生命周期时间重新计算(这时候k、v值一样)
	 * 4、当该用户这次请求jwt生成的token值已经超时，但该token对应cache中的k还是存在，则表示该用户一直在操作只是JWT的token失效了，程序会给token对应的k映射的v值重新生成JWTToken并覆盖v值，该缓存生命周期重新计算
	 * 5、当该用户这次请求jwt在生成的token值已经超时，并在cache中不存在对应的k，则表示该用户账户空闲超时，返回用户信息已失效，请重新登录。
	 * 6、每次当返回为true情况下，都会给Response的Header中设置Authorization，该Authorization映射的v为cache对应的v值。
	 * 7、注：当前端接收到Response的Header中的Authorization值会存储起来，作为以后请求token使用
	 * 8、sign为登录标识,0:系统用户(有效期30分钟)1:会员用户（有效期7天）
	 * 参考方案：https://blog.csdn.net/qq394829044/article/details/82763936
	 *
	 * @param mobile
	 * @param passWord
	 * @return
	 */
	public boolean jwtTokenRefresh(String token,String userId, String mobile, String passWord) {
		//缓冲中拿取token
		String cacheToken = String.valueOf(redisTemplate.opsForValue().get(CommonConstant.APP_USER_TOKEN + token + userId));

		if (oConvertUtils.isNotEmpty(cacheToken)) {
			//校验token有效性
			if (!JwtUtil.verifyIp(token, mobile, passWord)) {
				//生产新的token
				String newAuthorization = JwtUtil.sign( mobile, passWord);

				redisTemplate.opsForValue().set(CommonConstant.APP_USER_TOKEN + token + userId, newAuthorization, JwtUtil.APP_EXPIRE_TIME);
			} else {

				redisTemplate.opsForValue().set(CommonConstant.APP_USER_TOKEN + token + userId, cacheToken, JwtUtil.APP_EXPIRE_TIME);
			}
			return true;
		}
		return false;
	}


}

